package com.cksys.achievement.controller;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.cksys.achievement.entity.Result;
import com.cksys.achievement.entity.ResultPicture;
import com.cksys.achievement.entity.ResultUser;
import com.cksys.achievement.entity.User;
import com.cksys.achievement.enums.ResultStatus;
import com.cksys.achievement.service.*;
import com.cksys.achievement.service.impl.ResultServiceImpl;
import com.cksys.achievement.utils.FileUtils;
import com.cksys.achievement.utils.RestResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.core.parameters.P;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.util.HtmlUtils;

import javax.validation.Valid;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author tadechen
 * @since 2019-09-26
 */
@RestController
@RequestMapping("/result")
public class ResultController {

    private IUserService userService;
    private IResultUserService resultUserService;
    private IResultService resultService;
    private IResultPictureService resultPictureService;
    private FileUtils fileUtils;
    private ISettingService settingService;

    @Autowired
    public ResultController(IUserService userService, IResultService resultService, IResultUserService resultUserService,
                            IResultPictureService resultPictureService, FileUtils fileUtils, ISettingService settingService) {
        this.resultPictureService = resultPictureService;
        this.resultService = resultService;
        this.resultUserService = resultUserService;
        this.userService = userService;
        this.fileUtils = fileUtils;
        this.settingService = settingService;
    }

    /**
     * 添加成果
     * @param result 封装的成果bean
     * @param userIds 成果的参与者id
     * @param imgInfos 成果图片的简介数组
     * @param files 成果图片集合
     * @param userDetails 当前登录的对象
     * @throws IOException 如果出现异常直接进行事务回滚
     */
    @PostMapping("/add")
    @Transactional(rollbackFor = Exception.class)
    public RestResponse<String> addResult(@Valid Result result,
                                  @RequestParam(value = "userIds", required = false) Integer[] userIds,
                                  @RequestParam(value = "imgInfos", required = false) String[] imgInfos,
                                  @RequestParam(value = "imgs", required = false) MultipartFile[] files,
                                  @AuthenticationPrincipal UserDetails userDetails) throws IOException {
        User user = userService.getByUserName(userDetails.getUsername());
        result.setIsTop(false).setScore(0);
        if (user == null) {
            throw new RuntimeException("用户错误!");
        }

        if (settingService.getById(1).getAutoAudited()) {
            result.setResultStatus(ResultStatus.PASSED);
        } else {
            result.setResultStatus(ResultStatus.UNAUDITED);
        }

        result.setUId(user.getId()).setInsertTime(LocalDateTime.now());
        boolean save = resultService.save(result);

        Integer rId = result.getId();
        // 保存成果-用户对应表
        if (save) {
            List<ResultUser> resultUsers = new ArrayList<>();
            if (userIds != null) {
                resultUsers = Arrays.stream(userIds)
                        // 防止userIds乱传
                        .filter(t -> userService.isExist(t) && !t.equals(user.getId()))
                        .map(t -> new ResultUser().setUId(t).setRId(rId).setIsLeader(false))
                        .collect(Collectors.toList());
            }
            ArrayList<ResultUser> resultUsers1 = new ArrayList<>(resultUsers);
            resultUsers1.add(0, new ResultUser().setRId(rId).setUId(user.getId()).setIsLeader(true));
            resultUserService.saveBatch(resultUsers1);
        }

        // 保存成果-成果图片对应表
        if (files != null) {
            for (int i = 0; i < files.length; i++) {
                String s = fileUtils.saveImg(files[i], user);
                if (s != null) {
                    ResultPicture resultPicture = new ResultPicture().setInfo(imgInfos[i])
                            .setIsHidden(false).setRId(rId).setPath(s);
                    resultPictureService.save(resultPicture);
                }
            }
        }
        return RestResponse.ok("添加成功");
    }

    @PostMapping("/update")
    public RestResponse<String> update(@Valid Result result,
                               @RequestParam(value = "userIds", required = false) Integer[] userIds,
                               @RequestParam(value = "imgInfos", required = false) String[] imgInfos,
                               @RequestParam(value = "imgs", required = false) MultipartFile[] files,
                               @AuthenticationPrincipal UserDetails userDetails) throws IOException {
        User user = userService.getByUserName(userDetails.getUsername());
        if (user == null) {
            throw new RuntimeException("用户错误!");
        }
        ResultUser one = resultUserService.getOne(new QueryWrapper<ResultUser>().eq("u_id", user.getId()).eq("r_id", result.getId()));
        if (null == one || !one.getIsLeader()) {
            throw new RuntimeException("这不是您的成果！无法修改！");
        }

        boolean b = resultService.updateById(result);

        if (b) {
            // 更新成果-用户表
            List<ResultUser> resultUsers = resultUserService.list(new QueryWrapper<ResultUser>().eq("r_id", result.getId()).select("u_id"));
            List<Integer> resultUsersId = resultUsers.stream().map(ResultUser::getUId).collect(Collectors.toList());
            List<Integer> needRemove = resultUsers.stream()
                    .filter(t -> (!Arrays.asList(userIds).contains(t.getUId())) && (!t.getUId().equals(user.getId())))
                    .map(ResultUser::getUId)
                    .collect(Collectors.toList());
            if (needRemove.size() > 0) {
                resultUserService.remove(new QueryWrapper<ResultUser>().eq("r_id", result.getId()).in("u_id", needRemove));
            }

            List<ResultUser> needAdd = Arrays.stream(userIds)
                    .filter(t -> (!resultUsersId.contains(t)) && (!t.equals(user.getId())))
                    .map(t -> new ResultUser().setRId(result.getId()).setUId(t))
                    .collect(Collectors.toList());
            if (needAdd.size() > 0) {
                resultUserService.saveBatch(needAdd);
            }

            // 添加成果-图片
            if (files != null) {
                for (int i = 0; i < files.length; i++) {
                    String s = fileUtils.saveImg(files[i], user);
                    if (s != null) {
                        ResultPicture resultPicture = new ResultPicture().setInfo(imgInfos[i])
                                .setIsHidden(false).setRId(result.getId()).setPath(s);
                        resultPictureService.save(resultPicture);
                    }
                }
            }
        }

        return RestResponse.ok("更新成功");
    }

    @PostMapping("/delete/{id}")
    public RestResponse<String> delete(@PathVariable Integer id,
                                       @AuthenticationPrincipal UserDetails userDetails) {
        User user = userService.getByUserName(userDetails.getUsername());
        if (user == null) {
            throw new RuntimeException("用户错误!");
        }

        Result byId = resultService.getById(id);
        if (!byId.getUId().equals(user.getId())) {
            return RestResponse.fail("这不是您的成果！");
        } else {
            boolean b = resultService.removeById(id);
            if (b) {
                resultUserService.remove(new QueryWrapper<ResultUser>().eq("r_id", id));
                resultPictureService.remove(new QueryWrapper<ResultPicture>().eq("r_id", id));
                return RestResponse.ok("删除成功！");
            } else {
                return RestResponse.fail("删除失败！");
            }
        }
    }

    @GetMapping("/get/{id}")
    public RestResponse<Result> getById(@PathVariable Integer id) {
        return RestResponse.ok(resultService.getById(id));
    }

    @GetMapping("/getAll")
    public RestResponse<IPage<Result>> getAll(@RequestParam(value = "page", required = false, defaultValue = "1") Integer page,
                                             @RequestParam(value = "size", required = false, defaultValue = "10") Integer size) {
        IPage<Result> resultIPage = new Page<>(page, size);
        resultIPage = resultService.page(resultIPage, new LambdaQueryWrapper<Result>()
                .eq(Result::getResultStatus, ResultStatus.PASSED)
                .orderByDesc(Result::getGetTime, Result::getInsertTime));
        return RestResponse.ok(resultIPage);
    }

    @PostMapping("/search")
    public RestResponse<IPage<Result>> search(@RequestParam(value = "page", required = false, defaultValue = "1") Integer page,
                                             @RequestParam(value = "size", required = false, defaultValue = "10") Integer size,
                                             @RequestParam(value = "time", required = false) String time,
                                             @RequestParam(value = "type", required = false) Integer typeId,
                                             @RequestParam(value = "userIds[]", required = false) List<Integer> userIds) {
        IPage<Result> resultIPage = new Page<>(page, size);
        resultIPage = resultService.searchResultPage(resultIPage, time, typeId, userIds, null, ResultStatus.PASSED);
        return RestResponse.ok(resultIPage);
    }

    @PostMapping("/search/me")
    public RestResponse<IPage<Result>> search(@RequestParam(value = "page", required = false, defaultValue = "1") Integer page,
                                             @RequestParam(value = "size", required = false, defaultValue = "10") Integer size,
                                             @RequestParam(value = "time", required = false) String time,
                                             @RequestParam(value = "type", required = false) Integer typeId,
                                             @RequestParam(value = "resultStatus", required = false) ResultStatus resultStatus,
                                             @AuthenticationPrincipal UserDetails userDetails) {
        IPage<Result> resultIPage = new Page<>(page, size);
        User user = userService.getByUserName(userDetails.getUsername());
        if (user == null) {
            throw new RuntimeException("用户错误!");
        }
        Integer id = user.getId();
        IPage<Result> results = resultService.searchResultPage(resultIPage, time, typeId, null, id, resultStatus);
        return RestResponse.ok(results);
    }

    @GetMapping("/page")
    public RestResponse<IPage<Result>> page(@RequestParam(value = "page", required = false, defaultValue = "1") Integer page,
                                           @RequestParam(value = "size", required = false, defaultValue = "10") Integer size) {
        IPage<Result> resultPage = new Page<>(page, size);
        resultPage = resultService.page(resultPage, new LambdaQueryWrapper<Result>().orderByDesc(Result::getGetTime, Result::getInsertTime));
        return RestResponse.ok(resultPage);
    }

    @GetMapping("/list")
    public RestResponse<List<Result>> list() {
        LambdaQueryWrapper<Result> queryWrapper = new LambdaQueryWrapper<Result>()
                .eq(Result::getResultStatus, ResultStatus.PASSED)
                .orderByAsc(Result::getGetTime, Result::getInsertTime);
        return RestResponse.ok(resultService.list(queryWrapper));
    }

    @GetMapping("/top/get")
    public RestResponse<List<Result>> getTop() {
        LambdaQueryWrapper<Result> queryWrapper = new LambdaQueryWrapper<Result>()
                .eq(Result::getIsTop, true)
                .eq(Result::getResultStatus, ResultStatus.PASSED)
                .orderByAsc(Result::getGetTime, Result::getInsertTime)
                .last("limit 5");
        List<Result> list = resultService.list(queryWrapper);
        return RestResponse.ok(list);
    }

    @GetMapping("/user/top")
    public RestResponse<List<String>> getUserTop(@RequestParam("num") Integer num) {
        return RestResponse.ok(resultService.getTopUser(num));
    }

    /*--------管理员开始---------*/

    @PreAuthorize("hasAnyRole('ROLE_ADMIN', 'ROLE_SUPER')")
    @GetMapping("/count")
    public RestResponse count(@RequestParam(value = "resultStatus", required = false) ResultStatus resultStatus) {
        LambdaQueryWrapper<Result> queryWrapper = new LambdaQueryWrapper<>();
        if (resultStatus != null) {
            queryWrapper.eq(Result::getResultStatus, resultStatus);
        }
        return RestResponse.ok(resultService.count(queryWrapper));
    }

    @PreAuthorize("hasAnyRole('ROLE_ADMIN', 'ROLE_SUPER')")
    @PostMapping("/admin/delete/{id}")
    public RestResponse delete(@PathVariable Integer id) {
        Result byId = resultService.getById(id);
        boolean b = resultService.removeById(id);
        if (b) {
            resultUserService.remove(new QueryWrapper<ResultUser>().eq("r_id", id));
            resultPictureService.remove(new QueryWrapper<ResultPicture>().eq("r_id", id));
            return RestResponse.ok("删除成功！");
        } else {
            return RestResponse.fail("删除失败！");
        }
    }

    @PreAuthorize("hasAnyRole('ROLE_ADMIN', 'ROLE_SUPER')")
    @PostMapping("/admin/delete/more")
    public RestResponse deleteMore(@RequestParam("ids[]") List<Integer> ids) {
        boolean b = resultService.removeByIds(ids);
        if (b) {
            resultUserService.remove(new LambdaQueryWrapper<ResultUser>().in(ResultUser::getRId, ids));
            resultPictureService.remove(new LambdaQueryWrapper<ResultPicture>().in(ResultPicture::getRId, ids));
            return RestResponse.ok("删除成功！");
        } else {
            return RestResponse.fail("删除失败！");
        }
    }

    @PreAuthorize("hasAnyRole('ROLE_ADMIN', 'ROLE_SUPER')")
    @PostMapping("/admin/unaudited/{id}")
    public RestResponse unaudited(@PathVariable Integer id,
                                  @RequestParam("resultStatus") ResultStatus resultStatus) {
        if (resultStatus.getResultStatus().equals(0)) {
            resultStatus = ResultStatus.NOTPASS;
        }
        boolean b = resultService.updateById(new Result().setId(id).setResultStatus(resultStatus));
        if (b) {
            return RestResponse.ok();
        } else {
            return RestResponse.fail();
        }
    }

    @Transactional
    @PreAuthorize("hasAnyRole('ROLE_ADMIN', 'ROLE_SUPER')")
    @PostMapping("/admin/unaudited/more")
    public RestResponse unaudited(@RequestParam("ids[]") List<Integer> ids,
                                  @RequestParam("resultStatus") ResultStatus resultStatus) {
        if (resultStatus.getResultStatus().equals(0)) {
            resultStatus = ResultStatus.NOTPASS;
        }
        ResultStatus finalResultStatus = resultStatus;
        List<Result> results = ids.stream().map(t -> new Result().setId(t).setResultStatus(finalResultStatus)).collect(Collectors.toList());
        boolean b = resultService.updateBatchById(results);
        if (b) {
            return RestResponse.ok();
        } else {
            return RestResponse.fail();
        }
    }



    @PreAuthorize("hasAnyRole('ROLE_ADMIN', 'ROLE_SUPER')")
    @PostMapping("/admin/search")
    public RestResponse<IPage<Result>> search(@RequestParam(value = "page", required = false, defaultValue = "1") Integer page,
                                              @RequestParam(value = "size", required = false, defaultValue = "10") Integer size,
                                              @RequestParam(value = "time", required = false) String time,
                                              @RequestParam(value = "type", required = false) Integer typeId,
                                              @RequestParam(value = "userIds[]", required = false) List<Integer> userIds,
                                              @RequestParam(value = "resultStatus", required = false) ResultStatus resultStatus) {
        IPage<Result> resultIPage = new Page<>(page, size);
        resultIPage = resultService.searchResultPage(resultIPage, time, typeId, userIds, null, resultStatus);
        return RestResponse.ok(resultIPage);
    }

    @PreAuthorize("hasAnyRole('ROLE_ADMIN', 'ROLE_SUPER')")
    @GetMapping("/get/calendar")
    public RestResponse getCalendar() {
        return RestResponse.ok(resultService.getCalendar());
    }

    @PreAuthorize("hasAnyRole('ROLE_ADMIN', 'ROLE_SUPER')")
    @GetMapping("/get/byType")
    public RestResponse getByType() {
        return RestResponse.ok(resultService.getByType());
    }

    @PreAuthorize("hasAnyRole('ROLE_ADMIN', 'ROLE_SUPER')")
    @GetMapping("/get/byUser")
    public RestResponse getByUser() {
        return RestResponse.ok(resultService.getByUser());
    }

    @PreAuthorize("hasAnyRole('ROLE_ADMIN', 'ROLE_SUPER')")
    @GetMapping("/get/byTypeWithYear")
    public RestResponse getByUserWithYear() {
        return RestResponse.ok(resultService.getByTypeWithYear());
    }

    @PreAuthorize("hasAnyRole('ROLE_ADMIN', 'ROLE_SUPER')")
    @PostMapping("/top/update")
    public RestResponse updateTop(@RequestParam("ids[]") List<Integer> ids,
                                  @RequestParam("flag") Boolean flag) {
        List<Result> resultList = ids.stream().map(t -> new Result().setId(t).setIsTop(flag)).collect(Collectors.toList());
        boolean b = resultService.updateBatchById(resultList);
        if (b) {
            return RestResponse.ok();
        } else {
            return RestResponse.fail();
        }
    }
}
